unit idata;

interface
uses
  SysUtils;

const
  IOBUFFERSIZE            = 1024 * 4;

type
  EOIndustrialF16 = array[0..15] of Byte;       // 16 byte Float type

  PEOIndustrialTypeFloat = ^EOIndustrialTypeFloat;
  EOIndustrialTypeFloat = packed record
    Value: EOIndustrialF16;
    NDD: Byte;                                  // number of decimal digit 
  end;

  PEOBuffer = ^EOBuffer;
  EOBuffer = array[0..IOBUFFERSIZE] of Byte;

  EOIOBuffer = class
  private
    FBuffer: EOBuffer;
    FHead: Integer;
    FTail: Integer;

  public
    function Count: Integer;
    function Push(Value: Byte): Boolean; overload;
    function Push(Value: EOBuffer; Size: Integer): Boolean; overload;
    function Push(Value: EOIOBuffer): Boolean; overload;
    function Push(Value: String): Boolean; overload;

    function Pop(var Value: Byte): Boolean; overload;
    function Pop(var Value: EOBuffer): Boolean; overload;
    function Pop(var Value: String): Boolean; overload;

    function Peek(var Value: Byte): Boolean; overload;
    function Peek(var Value: EOBuffer): Boolean; overload;
    function Peek(var Value: String): Boolean; overload;

    procedure Flush;

    constructor Create;

  end;

  function StringToDouble(Value: String; NDD: Integer): Double;

  function IndustrialTypeFloatToDouble(Data: EOIndustrialTypeFloat): Double;
  procedure DoubleToIndustrialTypeFloat(Data: PEOIndustrialTypeFloat; Value: Double);

  function I2ToInt(Data: EOIndustrialF16): Integer;
  procedure IntToI2(var Data: EOIndustrialF16; Value: Integer);

  function I4ToInt(Data: EOIndustrialF16): Integer;
  procedure IntToI4(var Data: EOIndustrialF16; Value: Integer);

  function I6ToInt(Data: EOIndustrialF16): Integer;
  procedure IntToI6(var Data: EOIndustrialF16; Value: Integer);

  function I7ToInt(Data: EOIndustrialF16): Integer;
  procedure IntToI7(var Data: EOIndustrialF16; Value: Integer);

implementation

function EOIOBuffer.Count: Integer;
begin
  Result := FHead - FTail;
end;

function EOIOBuffer.Push(Value: Byte): Boolean;
begin
  if FHead > IOBUFFERSIZE then
    Result := False
  else begin
    Result := True;
    FBuffer[FHead] := Value;
    FHead := FHead + 1;
  end;
end;

function EOIOBuffer.Push(Value: EOBuffer; Size: Integer): Boolean;
begin
  if (FHead + Size) > IOBUFFERSIZE then
    Result := False
  else begin
    Result := True;
    Move(Value, FBuffer[FHead], Size);
    FHead := FHead + Size;
  end;
end;

function EOIOBuffer.Push(Value: EOIOBuffer): Boolean;
var
  tmpB: EOBuffer;
  Size: Integer;
begin
  Size := Value.Count;

  if (FHead + size) > IOBUFFERSIZE then
    Result := False
  else begin
    Result := True;
    FillChar(tmpB, SizeOf(EOBuffer), #0);
    Value.Peek(tmpB);
    Move(tmpB, FBuffer[FHead], Size);
    FHead := FHead + size;
  end;
end;

function EOIOBuffer.Push(Value: String): Boolean;
var
  Size: Integer;
begin
  Size := Length(Value);
  
  if (FHead + size) > IOBUFFERSIZE then
    Result := False
  else begin
    Result := True;
    Move(Value[1], FBuffer[FHead], Size);
    FHead := FHead + size;
  end;
end;

function EOIOBuffer.Pop(var Value: Byte): Boolean;
begin
  if FTail = FHead then
    Result := False
  else begin
    Result := True;
    Value := FBuffer[FTail];
    FTail := FTail + 1;
  end;
  if FTail = FHead then
    Flush;
end;

function EOIOBuffer.Pop(var Value: EOBuffer): Boolean;
begin
  if FTail = FHead then
    Result := False
  else begin
    Result := True;
    FillChar(Value, SizeOf(EOBuffer), #0);
    Move(FBuffer[FTail], Value, FHead);
    Flush;
  end;
end;

function EOIOBuffer.Pop(var Value: String): Boolean;
begin
  if FTail = FHead then
    Result := False
  else begin
    Result := True;
    FBuffer[FHead + 1] := 0;
    Value := PChar(@FBuffer[FTail]);
    SetLength(Value, Count);
    Flush;
  end;
end;

function EOIOBuffer.Peek(var Value: Byte): Boolean;
begin
  if FTail = FHead then
    Result := False
  else begin
    Result := True;
    Value := FBuffer[FTail];
  end;
end;

function EOIOBuffer.Peek(var Value: EOBuffer): Boolean;
begin
  if FTail = FHead then
    Result := False
  else begin
    Result := True;
    FillChar(Value, SizeOf(EOBuffer), #0);
    Move(FBuffer[FTail], Value, FHead);
  end;
end;

function EOIOBuffer.Peek(var Value: String): Boolean;
begin
  if FTail = FHead then
    Result := False
  else begin
    Result := True;
    FBuffer[FHead + 1] := 0;
    Value := PChar(@FBuffer[FTail]);
    SetLength(Value, Count);
  end;
end;

procedure EOIOBuffer.Flush;
begin
  FHead := 0;
  FTail := 0;
end;

constructor EOIOBuffer.Create;
begin
  Flush;
end;

// 12345678
// 00012345 3
// 00012.345
// 1
function StringToDouble(Value: String; NDD: Integer): Double;
begin
  Insert('.', Value, Length(Value)-(NDD-1));
  Result := StrToFloat(Value);
end;

function IndustrialTypeFloatToDouble(Data: EOIndustrialTypeFloat): Double;
var
  tmpS: String;
begin
  tmpS := Copy(String(PChar(@Data.Value)), 1, SizeOf(Data.Value));
  Insert('.', tmpS, Length(tmpS)-(Data.NDD-$30)+1);
  Result := StrToFloat(tmpS);
end;

procedure DoubleToIndustrialTypeFloat(Data: PEOIndustrialTypeFloat; Value: Double);
var
  tmpS: String;
begin
  // 0123456789
  // 01234567.89
  tmpS := StringOfChar('0', SizeOf(Data^.Value)-1);
  if (Data^.NDD-$30 > 0) then
    Insert('.', tmpS, SizeOf(Data^.Value) - (Data^.NDD-$30))
  else
    tmpS := tmpS + '0';

  // 123456789012345
  // 0.78654321
  tmpS := FormatFloat(tmpS, Value);
  Delete(tmpS, Pos('.', tmpS), 1);
  FillChar(Data^.Value, SizeOf(Data^.Value), '0');
  Move(tmpS[1], Data^.Value[SizeOf(Data^.Value)-Length(tmpS)], Length(tmpS));
end;

function I2ToInt(Data: EOIndustrialF16): Integer;
begin
  Result :=
    ((Data[14] - $30) * 10) +
    ((Data[15] - $30) * 1);
end;

procedure IntToI2(var Data: EOIndustrialF16; Value: Integer);
begin
  FillChar(Data, SizeOf(EOIndustrialF16), '0');

  if Value < 100 then begin
    Data[14] := Value mod 100   div 10   + $30;
    Data[15] := Value mod 10    div 1    + $30;
  end;
end;

function I4ToInt(Data: EOIndustrialF16): Integer;
begin
  Result :=
    ((Data[12] - $30) * 1000) +
    ((Data[13] - $30) * 100) +
    ((Data[14] - $30) * 10) +
    ((Data[15] - $30) * 1);
end;

procedure IntToI4(var Data: EOIndustrialF16; Value: Integer);
begin
  FillChar(Data, SizeOf(EOIndustrialF16), '0');

  if Value < 10000 then begin
    Data[12] := Value mod 10000 div 1000 + $30;
    Data[13] := Value mod 1000  div 100  + $30;
    Data[14] := Value mod 100   div 10   + $30;
    Data[15] := Value mod 10    div 1    + $30;
  end;
end;

function I6ToInt(Data: EOIndustrialF16): Integer;
begin
  Result :=
    ((Data[10] - $30) * 100000) +
    ((Data[11] - $30) * 10000) +
    ((Data[12] - $30) * 1000) +
    ((Data[13] - $30) * 100) +
    ((Data[14] - $30) * 10) +
    ((Data[15] - $30) * 1);
end;

procedure IntToI6(var Data: EOIndustrialF16; Value: Integer);
begin
  FillChar(Data, SizeOf(EOIndustrialF16), '0');

  if Value < 1000000 then begin
    Data[10] := Value mod 1000000 div 100000 + $30;
    Data[11] := Value mod 100000  div 10000  + $30;
    Data[12] := Value mod 10000   div 1000   + $30;
    Data[13] := Value mod 1000    div 100    + $30;
    Data[14] := Value mod 100     div 10     + $30;
    Data[15] := Value mod 10      div 1      + $30;
  end;
end;

function I7ToInt(Data: EOIndustrialF16): Integer;
begin
  Result :=
    ((Data[9]  - $30) * 1000000) +
    ((Data[10] - $30) * 100000) +
    ((Data[11] - $30) * 10000) +
    ((Data[12] - $30) * 1000) +
    ((Data[13] - $30) * 100) +
    ((Data[14] - $30) * 10) +
    ((Data[15] - $30) * 1);
end;

procedure IntToI7(var Data: EOIndustrialF16; Value: Integer);
begin
  FillChar(Data, SizeOf(EOIndustrialF16), '0');

  if Value < 10000000 then begin
    Data[9]  := Value mod 10000000 div 1000000 + $30;
    Data[10] := Value mod 1000000  div 100000  + $30;
    Data[11] := Value mod 100000   div 10000   + $30;
    Data[12] := Value mod 10000    div 1000    + $30;
    Data[13] := Value mod 1000     div 100     + $30;
    Data[14] := Value mod 100      div 10      + $30;
    Data[15] := Value mod 10       div 1       + $30;
  end;
end;

end.
